package com.me.myFramework.myAop.aop301;

import com.me.myFramework.myAop.aop301.advice.Advice;
import com.me.myFramework.myAop.aop301.advice.method.AfterReturningAdvice;
import com.me.myFramework.myAop.aop301.advice.method.MethodBeforeAdvice;
import com.me.myFramework.myAop.aop301.intercept.method.*;
import org.springframework.aop.Pointcut;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

/**
 * 自己实现 AOP-3：  <p></p>
 *
 * 用来生成 AOP代理对象的工厂
 *
 * @author ME
 * @date 2021/9/27
 */
public class ProxyFactory {

    /** 目标对象 */
    private Object target;

    /** 目标类 */
    private final Class<?> targetClass;

    /**
     * 一个完整的切面对象（pointcut + advice）
     */
    private final List<PointcutAdvisor> advisors = new ArrayList<>();

    public ProxyFactory(Object target) {
        this.target = target;
        this.targetClass = target.getClass();
    }

    /**
     * 动态代理织入增强逻辑，逻辑同 JdkDynamicAopProxy#getProxy(java.lang.ClassLoader)
     */
    public Object getProxy(ClassLoader classLoader) {
        Object proxy = null;

        List<MethodInterceptor> interceptorList = getInterceptors(null, targetClass);
        for (int i = interceptorList.size() - 1; i >= 0; i--) {
            MethodInterceptor interceptor = interceptorList.get(i);
            proxy = Proxy.newProxyInstance(classLoader, targetClass.getInterfaces(), new InvocationHandler() {
                private final Object curTarget = target;

                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    ReflectiveMethodInvocation mi = new ReflectiveMethodInvocation(proxy, curTarget, method, args, interceptor);
                    return mi.proceed();
                }
            });
            this.target = proxy;
        }

        return proxy;
    }

    /**
     * TODO 在 Spring实现中，此方法会使用 正在执行的方法 去匹配 切点表达式（pointcut） 并筛选出适用的 advice（增强），并转换成 拦截器（执行增强逻辑的执行器）。
     *  而代理套代理实现的多切面是没法这么做的，因为最开始获得代理对象的时候不知道会执行哪个方法，只能遇到一个切面配置生成一层代理，真正执行的时候再校验 执行方法时候匹配 切点表达式（pointcut）。
     *  代理套代理的另一个问题是性能问题，每一个切面配置都需要生成一次代理类，而 Spring实现只会生成一个代理类
     *
     * 逻辑类似 AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice(java.lang.reflect.Method, java.lang.Class)
     *
     * @param method        正在执行的方法
     * @param targetClass   目标类
     * @return  匹配当前方法的拦截器
     */
    private List<MethodInterceptor> getInterceptors(Method method, Class<?> targetClass) {
        List<MethodInterceptor> interceptorList = new ArrayList<>(this.advisors.size());
        for (PointcutAdvisor advisor : this.advisors) {
            Pointcut pointcut = advisor.getPointcut();
            if (pointcut.getClassFilter().matches(targetClass)) {
            // if (pointcut.getClassFilter().matches(targetClass) && pointcut.getMethodMatcher().matches(method, targetClass)) {
                Advice advice = advisor.getAdvice();
                if (advice instanceof MethodBeforeAdvice) {
                    // Spring 中是通过适配器（MethodBeforeAdviceAdapter）把 advice转换成拦截器的
                    interceptorList.add(new MethodBeforeAdviceInterceptor((MethodBeforeAdvice) advice));
                }
                else if (advice instanceof AfterReturningAdvice) {
                    interceptorList.add(new AfterReturningAdviceInterceptor((AfterReturningAdvice) advice));
                }
                else {
                    throw new RuntimeException("未知的 advice类型");
                }
            }
        }

        return interceptorList;
    }

    public void addAdvisor(PointcutAdvisor advisor) {
        this.advisors.add(advisor);
    }

}
